package com.ruoyi.common.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 * 
 * @author ruoyi
 */
@Component
public class JwtTokenUtil
{
    @Value("${jwt.secret:ruoyi-secret-key-must-be-at-least-256-bits-long-for-hs256-algorithm}")
    private String secret;

    @Value("${jwt.expiration:86400}") // 默认24小时
    private Long expiration;

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_USER_ID = "userId";
    private static final String CLAIM_KEY_CREATED = "created";

    /**
     * 从token中获取用户名
     */
    public String getUsernameFromToken(String token)
    {
        String username;
        try
        {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        }
        catch (Exception e)
        {
            username = null;
        }
        return username;
    }

    /**
     * 从token中获取用户ID
     */
    public Long getUserIdFromToken(String token)
    {
        Long userId;
        try
        {
            Claims claims = getClaimsFromToken(token);
            userId = claims.get(CLAIM_KEY_USER_ID, Long.class);
        }
        catch (Exception e)
        {
            userId = null;
        }
        return userId;
    }

    /**
     * 从token中获取过期时间
     */
    public Date getExpirationDateFromToken(String token)
    {
        Date expiration;
        try
        {
            Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        }
        catch (Exception e)
        {
            expiration = null;
        }
        return expiration;
    }

    /**
     * 从token中获取Claims
     */
    private Claims getClaimsFromToken(String token)
    {
        Claims claims;
        try
        {
            claims = Jwts.parserBuilder()
                    .setSigningKey(getSecretKey())
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        }
        catch (Exception e)
        {
            claims = null;
        }
        return claims;
    }

    /**
     * 判断token是否过期
     */
    private Boolean isTokenExpired(String token)
    {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    /**
     * 生成token
     */
    public String generateToken(Long userId, String username)
    {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USER_ID, userId);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(username, claims);
    }

    /**
     * 生成token
     */
    private String generateToken(String subject, Map<String, Object> claims)
    {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(getSecretKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 验证token
     */
    public Boolean validateToken(String token, String username)
    {
        String tokenUsername = getUsernameFromToken(token);
        return (tokenUsername.equals(username) && !isTokenExpired(token));
    }

    /**
     * 刷新token
     */
    public String refreshToken(String token)
    {
        String refreshedToken;
        try
        {
            Claims claims = getClaimsFromToken(token);
            claims.put(CLAIM_KEY_CREATED, new Date());
            refreshedToken = generateToken(claims.getSubject(), claims);
        }
        catch (Exception e)
        {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 获取密钥
     */
    private SecretKey getSecretKey()
    {
        return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
    }
}

